﻿using System.Collections.Generic;
using System.Linq;
using HK.Core.Extend;
using HK.Core.GUIEx.Protocol.Attribute;
using HK.Editor.Common;
using HK.Editor.GUIEx.Protocol;
using HK.Editor.GUIEx.Protocol.Drawer;
using HK.Editor.GUIEx.Protocol.Pagination;
using HK.Editor.GUIEx.Protocol.Selection;
using HK.Editor.GUIEx.Styles;
using HK.Editor.Utils;
using UnityEditor;
using UnityEditor.IMGUI.Controls;
using UnityEngine;

namespace HK.Editor.GUIEx.Drawer
{
    /// <summary>
    /// GUI绘制器 - 分页面板
    /// </summary>
    public abstract class PaginationDrawer<TType> : GUIDiyDrawer, IPaginationDrawer
    {
        /// <summary>
        /// 保护构造函数
        /// </summary>
        /// <param name="iDrawFromUnity">是否从Unity调用绘制标志位</param>
        /// <param name="iOptions">选项</param>
        protected PaginationDrawer(bool iDrawFromUnity = false, int iOptions = (int)GUIOperateOptions.None) : 
            base(iDrawFromUnity, iOptions) {}

        /// <summary>
        /// 保护构造函数
        /// </summary>
        /// <param name="iSelfProperty">自身Property</param>
        /// <param name="iPagination">分页设定</param>
        /// <param name="iOnSearched">检索回调</param>
        /// <param name="iDrawFromUnity">是否从Unity调用绘制标志位</param>
        /// <param name="iOptions">选项</param>
        protected PaginationDrawer(SerializedProperty iSelfProperty, IPagination iPagination,
            OnSearchBtnClicked iOnSearched = null, bool iDrawFromUnity = false, 
            int iOptions = (int)GUIOperateOptions.None) : base(iSelfProperty, iDrawFromUnity, iOptions)
        {
            Pagination = iPagination;
            OnSearched = iOnSearched;
        }

        /// <summary>
        /// 数组标志位
        /// </summary>
        public virtual bool IsArray => true;

        /// <summary>
        /// Footer可见标志位
        /// </summary>
        public virtual bool FooterVisible => Addable || Removable;

        /// <summary>
        /// 分页设定
        /// </summary>
        public IPagination Pagination { get; protected set; } = null;

#region Size&Index

        /// <summary>
        /// 总个数
        /// </summary>
        public virtual int TotalSize => -1;
        /// <summary>
        /// 可见数量
        /// <para>* 分页的场合，为当前分页数量</para>
        /// </summary>
        public virtual int VisibleSize
        {
            get
            {
                if (!Searching)
                {
                    return TotalSize;
                }
                return SearchResult?.Count ?? 0;
            }
        }

        /// <summary>
        /// 开始坐标偏移 - Y轴
        /// </summary>
        public virtual float StartPosYOffset => 0.0f;

        /// <summary>
        /// 重置页索引
        /// </summary>
        public void ResetIndex() => Pagination.ResetIndex();
        
        /// <summary>
        /// 取得游侠范围
        /// </summary>
        /// <param name="oStartIndex">Element开始索引</param>
        /// <param name="oEndIndex">Element结束索引</param>
        /// <returns>true:OK; false:NG;</returns>
        protected bool GetVisibleRange(out int oStartIndex, out int oEndIndex)
        {
            oStartIndex = 0;
            oEndIndex = 0;
            if (!IsArray) return false;
            
            return GetVisibleRange(VisibleSize, out oStartIndex, out oEndIndex);
        }

        /// <summary>
        /// 取得当前分页可视范围
        /// </summary>
        /// <param name="iElementNum">元素个数</param>
        /// <param name="oStartIndex">开始索引(从0开始)</param>
        /// <param name="oEndIndex">结束索引s</param>
        /// <returns>当前分页可视范围</returns>
        public bool GetVisibleRange(int iElementNum, out int oStartIndex, out int oEndIndex) => 
            Pagination.GetVisibleRange(iElementNum, out oStartIndex, out oEndIndex);

#endregion

#region TTypes

        /// <summary>
        /// 取得Element的值
        /// </summary>
        /// <param name="iIndex">索引</param>
        /// <returns>Element的值</returns>
        protected abstract TType GetElementValue(int iIndex);

        /// <summary>
        /// 查找指定Element的索引
        /// </summary>
        /// <param name="iElement">Element</param>
        /// <returns>Element索引</returns>
        protected abstract int IndexOf(TType iElement);

#endregion

#region Rect

        /// <summary>
        /// 高度 : 空列表高度
        /// </summary>
        protected static readonly float EmptyListHeight = 40.0f;

        /// <summary>
        /// 高度 -  检索Bar
        /// </summary>
        private static readonly float SearchBarHeight = HeaderHeight;
        
        /// <summary>
        /// 高度 -  Footer
        /// </summary>
        private static readonly float FooterHeight = HeaderHeight;

        /// <summary>
        /// 按钮宽度 - 检索
        /// </summary>
        private static readonly float _searchBtnWidth = BtnWidthShort;
        
        /// <summary>
        /// 取得头部高度
        /// </summary>
        public float GetHeaderHeight() => HeaderHeight;
        
        /// <summary>
        /// 取得高度 - Footer
        /// </summary>
        public float GetFooterHeight() => FooterHeight;
        
        /// <summary>
        /// 取得高度
        /// <para>* 包含子节点及其展开/不展开的实际高度</para>
        /// </summary>
        /// <returns>高度</returns>
        public override float CalcTotalHeight()
        {
            var totalHeight = HeaderHeight;
            if (SearchBarVisible)
            {
                totalHeight += SearchBarHeight;
            }

            // 展开,则计算子节点高度
            if (IsExpanded)
            {
                var elementsTotalHeight = CalcElementsTotalHeight(SelfProperty, out var oValidCount);
                totalHeight += elementsTotalHeight + 1;
            
                // Footer
                if (FooterVisible)
                {
                    totalHeight += FooterHeight + 4;
                }
                if (!DrawFromUnity)
                {
                    totalHeight += 4;
                }
            }
            
            TotalHeight = totalHeight;
            return TotalHeight;
        }

        /// <summary>
        /// 计算子节点元素的总共高度
        /// </summary>
        /// <param name="iParent">父节点Property</param>
        /// <param name="oValidCount">有效个数</param>
        /// <returns>子节点元素的总共高度</returns>
        protected abstract float CalcElementsTotalHeight(SerializedProperty iParent, out int oValidCount);

        /// <summary>
        /// 取得Element高度
        /// </summary>
        /// <param name="iIndex">Element索引</param>
        /// <param name="iElement">Element</param>
        /// <returns>Element高度</returns>
        protected abstract float GetElementHeight(int iIndex, TType iElement);

        /// <summary>
        /// 更新列表拖拽区域信息
        /// </summary>
        /// <param name="iDragPosition">拖拽坐标</param>
        /// <param name="iStartIndex">开始索引</param>
        /// <param name="iEndIndex">结束索引</param>
        protected abstract void UpdateListDragRects(float iDragPosition, int iStartIndex, int iEndIndex);
        
        /// <summary>
        /// 重置成员Element矩形区域数据
        /// </summary>
        /// <param name="iSize">大小</param>
        protected override void ResizeElementRects(int iSize)
        {
            base.ResizeElementRects(iSize);
            _elementsControlIds.Clear();
        }
        
        /// <summary>
        /// 更新Element的绘制矩形区域
        /// </summary>
        /// <param name="iRect">矩形区域</param>
        /// <param name="iEvent">事件</param>
        protected void UpdateElementRects(Rect iRect, Event iEvent) 
        {
            // resize array if elements changed
            var len = VisibleSize;
			
            // 长度有变化，则重置数组大小
            if (len != ElementRects.Length)
            {
                ResizeElementRects(len);
            }

            // 重新绘制
            if (EventType.Repaint == iEvent.type) 
            {

                //start rect
                var elementRect = iRect;
                elementRect.yMin = elementRect.yMax = iRect.yMin + 1;
                //elementRect.y += VerticalSpacing;
                //var spacing = VerticalSpacing;
				
                // TODO:追加分页处理
                // 取得分页可视范围
                if(!GetVisibleRange(out var oStartIndex, out var oEndIndex)) return;

                // 遍历
                for (var idx = oStartIndex; idx < oEndIndex; ++idx) 
                {
                    var element = GetElementValue(idx);
                    if(null == element) continue;
                    
                    elementRect.y = elementRect.yMax;
                    elementRect.height = GetElementHeight(idx, element);
                    ElementRects[idx] = elementRect;
                    elementRect.yMax += 1;
					
                    // 保存Element的ControlId
                    var targetIndex = idx;
                    if (Searching)
                    {
                        targetIndex = SearchResult[idx].Index;
                    }
                    if (!SaveElementControlId(targetIndex, element, elementRect))
                    {
                        Warning("UpdateElementRects():SaveElementControlId() Failed!(Index:{0}/{1})", idx, targetIndex);
                    }
                }
            }
        }

#endregion
        
#region Options

        /// <summary>
        /// 可排序
        /// </summary>
        public override bool Sortable
        {
            get
            {
                if (!IsExpanded) return false;
                if (0 > VisibleSize) return false;
                return base.Sortable;
            }
        }

        /// <summary>
        /// 可检索
        /// </summary>
        public override bool Searchable 
        {
            get
            {
                if (!IsExpanded) return false;
                if (0 > VisibleSize) return false; 
                return base.Searchable;
            }
        }

#endregion
        
#region Contents

        /// <summary>
        /// 标题
        /// </summary>
        private GUIContent _emptyLabelContent = null;
        
        /// <summary>
        /// 分页信息标题
        /// <para>* 数据格式示例 : 1/9</para>
        /// </summary>
        private GUIContent _pageInfoContent = null;

        /// <summary>
        /// 分页大小信息
        /// </summary>
        private GUIContent _pageSizeContent = null;
        
        /// <summary>
        /// 锁定按钮 - IconOnly
        /// </summary>
        protected GUIContent LockIconBtnContent { get; private set; } = null;
        
        /// <summary>
        /// 解锁按钮 - IconOnly
        /// </summary>
        protected GUIContent UnLockIconBtnContent { get; private set; } = null;

        /// <summary>
        /// 按钮 - 检索
        /// </summary>
        protected GUIContent SearchBtnContent { get; private set; } = null;
        
        protected Dictionary<int, GUIContent> ElementsCaptions = new Dictionary<int, GUIContent>();        
        /// <summary>
        /// 刷新生成标题列表
        /// </summary>
        protected void RefreshElementCaptions()
        {
            ElementsCaptions.Clear();
            if (!IsArray || 0 >= VisibleSize)
            {
                return;
            }

            GetVisibleRange(VisibleSize, out var oStartIndex, out var oEndIndex);
            for (var idx = oStartIndex; idx < oEndIndex; ++idx)
            {
                var childProperty = GetElementValue(idx);
                if(null == childProperty) continue;
                GetElementCaption(idx, childProperty);
            }
        }
        
        /// <summary>
        /// 取得Element标题
        /// </summary>
        /// <param name="iIndex">Element索引</param>
        /// <param name="iChildProperty">Child Property</param>
        /// <returns>Element标题</returns>
        protected abstract GUIContent GetElementCaption(int iIndex, TType iChildProperty);

        /// <summary>
        /// 初始化GUIContent
        /// </summary>
        protected override void InitContents()
        {
            base.InitContents();
            
            var emptyLabel = IsArray ? "List is empty!" : "This is not list type!";
            _emptyLabelContent = UtilsGUIEx.CreateGuiContent(emptyLabel);
            
            // 加锁/解锁
            LockIconBtnContent = UtilsGUIEx.CreateGuiContentWithIcon("", EditorConst.IconLock);
            UnLockIconBtnContent = UtilsGUIEx.CreateGuiContentWithIcon("", EditorConst.IconUnLock);
            
            // 检索按钮
            SearchBtnContent = UtilsGUIEx.CreateGuiContentWithIcon("检索", EditorConst.IconBrowse);
            
            // 分页
            _pageInfoContent = UtilsGUIEx.CreateGuiContent();
            _pageSizeContent = UtilsGUIEx.CreateGuiContent();
        }

#endregion

#region Controls

        /// <summary>
        /// 成员Element的ControlId列表
        /// <para>* Key : Element索引</para>
        /// <para>* Value : Control Id</para>
        /// </summary>
        private Dictionary<int, int> _elementsControlIds = new Dictionary<int, int>();

        /// <summary>
        /// 取得成员Element的ControlId
        /// </summary>
        /// <param name="iIndex">Element索引</param>
        /// <returns>成员Element的ControlId</returns>
        protected int GetElementControlId(int iIndex) =>
            _elementsControlIds.TryGetValue(iIndex, out var oControlId) ? oControlId : -1;

        /// <summary>
        /// 保存成员Element的ControlId
        /// </summary>
        /// <param name="iIndex">Element索引</param>
        /// <param name="iTarget">目标成员</param>
        /// <param name="iRect">显示矩形区域</param>
        /// <returns>true:OK; false:NG;</returns>
        protected bool SaveElementControlId(int iIndex, TType iTarget, Rect iRect)
        {
            if (_elementsControlIds.TryGetValue(iIndex, out var oControlId))
            {
                if (-1 != oControlId) return true;
                
                var hashCode = GenerateElementHashCode(iIndex, iTarget);
                if (-1 == hashCode) return false;

                var controlId = GenerateControlId(iRect, hashCode);
                if (-1 == controlId) return false;
                
                _elementsControlIds[iIndex] = controlId;
            }
            else
            {
                var hashCode = GenerateElementHashCode(iIndex, iTarget);
                if (-1 == hashCode) return false;

                var controlId = GenerateControlId(iRect, hashCode);
                if (-1 == controlId) return false;
                
                _elementsControlIds.Add(iIndex, controlId);
            }

            return true;
        }

        /// <summary>
        /// 取得Element的索引
        /// </summary>
        /// <param name="iControlId">Control Id</param>
        /// <returns>Element的索引</returns>
        protected int GetElementIndexByControlId(int iControlId)
        {
            if (-1 >= iControlId) return 0;

            var targetIndex = 0;
            var count = _elementsControlIds.Count;
            for (var idx = 0; idx < count; ++idx)
            {
                var loopIndex = _elementsControlIds.Keys.ElementAt(idx);
                var loopControlId = _elementsControlIds.Values.ElementAt(idx);
                if(iControlId != loopControlId) continue;
                targetIndex = loopIndex;
                break;
            }

            return targetIndex;
        }

        /// <summary>
        /// 生成成员Element的HashCode
        /// </summary>
        /// <param name="iIndex">成员Element的索引</param>
        /// <param name="iTarget">目标成员</param>
        /// <returns>成员Element的HashCode</returns>
        protected virtual int GenerateElementHashCode(int iIndex, TType iTarget) => -1;
        
#endregion

#region Events

        /// <summary>
        /// 移动分页
        /// </summary>
        /// <param name="iPageIndex">分页索引</param>
        private void OnMovePageClicked(object iPageIndex) 
        {
            Pagination.Index = (int)iPageIndex;
        }
        
        /// <summary>
        /// 展开/缩进
        /// </summary>
        /// <param name="iIsExpanded">true:展开; false:缩进;</param>
        public void Expand(bool iIsExpanded)
        {
            IsExpanded = iIsExpanded;
            if (null != SelfProperty)
            {
                SelfProperty.isExpanded = iIsExpanded;
            }
        }

        /// <summary>
        /// 拖拽事件
        /// </summary>
        /// <param name="iAssets">拖拽的资产列表(如:文件/文件夹等)</param>
        protected virtual void OnAssetsDragAndDropDown(string[] iAssets) {}

        /// <summary>
        /// 检索按钮点击事件
        /// </summary>
        public OnSearchBtnClicked OnSearched { get; protected set; } = null;

        /// <summary>
        /// 删除Element按钮点击时间
        /// </summary>
        protected abstract void OnDelElementBtnClicked();

        /// <summary>
        /// 追加Element按钮点击时间
        /// </summary>
        protected virtual void OnAddElementBtnClicked() {}
        
        /// <summary>
        /// 移除Element
        /// </summary>
        /// <param name="iIndex">索引</param>
        protected abstract void RemoveElementByIndex(int iIndex);

#endregion

#region Lock

        /// <summary>
        /// Element枷锁状态
        /// <para>* Key : Element索引</para>
        /// <para>* Value : 锁定/未锁定标志位</para>
        /// </summary>
        private Dictionary<int, bool> _elementsLockStates = new Dictionary<int, bool>();

        /// <summary>
        /// 判断指定索引的Element是否被锁定
        /// </summary>
        /// <param name="iIndex">Element索引</param>
        /// <returns>true:锁定; false:未锁定;</returns>
        protected bool IsElementLocked(int iIndex)
        {
            if (!Lockable) return false;
            if (-1 >= iIndex || iIndex >= TotalSize) return false;
                    
            // 若尚未存在，则默认未锁定状态
            if (0 >= _elementsLockStates.Count)
            {
                SetElementLockState(iIndex, true);
                return true;
            }
            if (!_elementsLockStates.TryGetValue(iIndex, out var oLocked))
            {
                SetElementLockState(iIndex, true);
                return true;
            }
                    
            return oLocked;
        }

        /// <summary>
        /// 设定Element的锁定状态
        /// </summary>
        /// <param name="iIndex">Element索引</param>
        /// <param name="iLocked">true:锁定; false:未锁定;</param>
        protected void SetElementLockState(int iIndex, bool iLocked)
        {
            if (-1 >= iIndex || iIndex >= TotalSize) return;

            if (0 >= _elementsLockStates.Count)
            {
                _elementsLockStates.Add(iIndex, iLocked);
                return;
            }
            
            _elementsLockStates[iIndex] = iLocked;
        }

#endregion

#region Selection
        
        /// <summary>
        /// 多个可选标志位
        /// <para>* true:可同时选中多个对象; false:不可选中多个;</para>
        /// </summary>
        private bool _multipleSelection = false;
        
        /// <summary>
        /// 取得指定位置的Element的索引
        /// </summary>
        /// <param name="iMousePosition">鼠标点击位置</param>
        /// <returns>-1:无效; 其他:(非列表 - 0:代表自身被点击; 列表 - 成员Element的索引);</returns>
        protected override int MouseClickCheck(Vector2 iMousePosition) 
        {
            // 取得可视范围
            if (!GetVisibleRange(out var oStartIndex, out var oEndIndex)) return -1;
            
            // 遍历
            for (var idx = oStartIndex; idx < oEndIndex; ++idx) 
            {
                var rect = ElementRects[idx];
                if (rect.Contains(iMousePosition) || 
                    (idx == 0 && iMousePosition.y <= rect.yMin) ||               // 低于第一个，则视为点击第一个
                    (idx == oEndIndex - 1 && iMousePosition.y >= rect.yMax))     // 超过最后一个，则视为点击最后一个
                {
                    return idx;
                }
            }

            return -1;
        }
        
        /// <summary>
        /// 校验是否能被选中
        /// <para>* 仅仅校验Element的索引在不在合法范围内</para>
        /// </summary>
        /// <param name="iIndex">Element索引</param>
        /// <returns>true:能被选中; false:反之;</returns>
        protected override bool CanSelect(int iIndex) 
        {
            return iIndex >= 0 && iIndex < VisibleSize;
        }
        
        /// <summary>
        /// 判断指定位置是否在Element范围之内
        /// </summary>
        /// <param name="iPosition">位置</param>
        /// <param name="iIndex">Element索引</param>
        /// <returns>true:在Element范围内; false:反之;</returns>
        private bool IsPositionWithinElement(Vector2 iPosition, int iIndex) 
        {
            return CanSelect(iIndex) && ElementRects[iIndex].Contains(iPosition);
        }		
        
        /// <summary>
        /// 判断鼠标左键/中键按下？
        /// </summary>
        /// <param name="iEvent">Event</param>
        /// <returns>true:按下; false:未按下;</returns>
        private bool IsMouseLeftOrMidBtnDown(Event iEvent) 
        {
            // 鼠标左键/中键按下
            return iEvent.button == 0 || iEvent.button == 2;
        }
        
        /// <summary>
        /// 校验是否能被选中
        /// </summary>
        /// <param name="iPosition">位置</param>
        /// <returns>true:能被选中; false:反之;</returns>
        private bool CanSelect(Vector2 iPosition) 
        {
            return ListSelection.Length > 0 && ListSelection.Any(iIndex => IsPositionWithinElement(iPosition, iIndex));
        }

        /// <summary>
        /// 列表Element选中回调
        /// </summary>
        public ListElementSelected<TType> ElementSelected { get; private set; } = null;
        
        /// <summary>
        /// 列表选中处理 - 后处理
        /// </summary>
        /// <param name="iRect">绘制矩形区域</param>
        /// <param name="iEvent">Event</param>
        protected override void HandlePostSelection(Rect iRect, Event iEvent) 
		{

			switch (iEvent.GetTypeForControl(ControlId)) 
			{
				// 鼠标按下
				case EventType.MouseDown:

					if (iRect.Contains(iEvent.mousePosition) && IsMouseLeftOrMidBtnDown(iEvent)) 
					{
						// 取得选中Element的索引
						var index = MouseClickCheck(iEvent.mousePosition);
						if (CanSelect(index)) 
						{
							// 当前Keyboard无焦点，或者当前焦点目标为当前对象，或者鼠标中间按下
							DoSelection(index, GUIUtility.keyboardControl == 0 || GUIUtility.keyboardControl == ControlId || iEvent.button == 2, iEvent);
						}
						else 
						{
							// 清空选中信息
							ListSelection.Clear();
						}

						HandleUtility.Repaint();
					}

					break;

				// 鼠标抬起
				case EventType.MouseUp:

					// 不可拖拽
					if (!Draggable) 
					{
						// select the single object if no selection modifier is being performed
						// 选中当前按下所选中Element的索引
                        ListSelection.SelectWhenNoAction(PressIndex, iEvent);
					}
					// 当前对象被选中
					else if (GUIUtility.hotControl == ControlId) 
					{
						iEvent.Use();
						// 拖拽中
						if (Dragging) 
						{
                            Dragging = false;
							//move elements in list
							ReorderDraggedElements(DragDirection, DragRectsList.StartIndex, () => DragRectsList.SortByPosition());
						}
						else 
						{

							// if we didn't drag, then select the original pressed object
                            ListSelection.SelectWhenNoAction(PressIndex, iEvent);
						}

						GUIUtility.hotControl = 0;
					}

					HandleUtility.Repaint();

					break;

				// 键盘按键按下
				case EventType.KeyDown:

					if (GUIUtility.keyboardControl == ControlId) 
					{
						// 向下箭头按下
						if (iEvent.keyCode == KeyCode.DownArrow && !Dragging) 
						{
                            ListSelection.Select(Mathf.Min(ListSelection.Last + 1, VisibleSize - 1));
							iEvent.Use();
						}
						// 向上箭头按下
						else if (iEvent.keyCode == KeyCode.UpArrow && !Dragging) 
						{

                            ListSelection.Select(Mathf.Max(ListSelection.Last - 1, 0));
							iEvent.Use();
						}
						// Esc键按下
						else if (iEvent.keyCode == KeyCode.Escape && GUIUtility.hotControl == ControlId) 
						{

							GUIUtility.hotControl = 0;
							if (Dragging) 
							{
                                Dragging = false;
                                ListSelection = PrevListSelection;
							}

							iEvent.Use();
						}
					}

					break;
			}
		}
        
        /// <summary>
        /// 设置选中
        /// </summary>
        /// <param name="iIndex">选中Element索引</param>
        /// <param name="iSetKeyboardControl">设置Keyboard</param>
        /// <param name="iEvent">Event</param>
        protected override void DoSelection(int iIndex, bool iSetKeyboardControl, Event iEvent) 
        {
            // append selections based on action, this may be a additive (ctrl) or range (shift) selection
            if (_multipleSelection) 
            {
                ListSelection.AppendWithAction(PressIndex = iIndex, iEvent);
            }
            else 
            {
                ListSelection.Select(PressIndex = iIndex);
            }

            // 回调
            // onSelectCallback?.Invoke(this);

            if (Draggable) 
            {
                Dragging = false;
                DragPosition = PressPosition = iEvent.mousePosition.y;
                if (!GetVisibleRange(out var oStartIndex, out var oEndIndex))
                {
                    iEvent.Use();
                    return;
                }
                
                UpdateListDragRects(DragPosition, oStartIndex, oEndIndex);

                // 移除开始与结束区间以外的信息
                ListSelection.Trim(oStartIndex, oEndIndex);
                PrevListSelection = ListSelection.Clone();

                GUIUtility.hotControl = ControlId;
            }

            // 设置Keyboard
            if (iSetKeyboardControl) 
            {
                GUIUtility.keyboardControl = ControlId;
            }

            iEvent.Use();
        }

#endregion

#region Drag

        /// <summary>
        /// 重现排序
        /// </summary>
        /// <param name="iDirection">方向(1:向后移动; -1:向前移动;)</param>
        /// <param name="iOffset">偏移</param>
        /// <param name="iSortList">列表排序回调</param>
        protected override void ReorderDraggedElements(int iDirection, int iOffset, System.Action iSortList) 
        {
            if(null == SelfProperty) return;
                    
            // save the current expanded states on all elements. I don't see any other way to do this
            // MoveArrayElement does not move the foldout states, so... fun.
            DragRectsList.RecordState();

            // 排序
            iSortList?.Invoke();

            ListSelection.Sort((iA, iB) => {

                var d1 = DragRectsList.GetIndexFromSelection(iA);
                var d2 = DragRectsList.GetIndexFromSelection(iB);
                return iDirection > 0 ? d1.CompareTo(d2) : d2.CompareTo(d1);
            });

            // swap the selected elements in the List
            var s = ListSelection.Length;
            while (--s > -1) 
            {
                var newIndex = DragRectsList.GetIndexFromSelection(ListSelection[s]);
                var listIndex = newIndex + iOffset;
                ListSelection[s] = listIndex;
                SelfProperty.MoveArrayElement(DragRectsList[newIndex].StartIndex, listIndex);
            }

            // restore expanded states on items
            // 恢复展开信息
            DragRectsList.RestoreState(SelfProperty);

            // 应用重排序信息
            ApplyReorder();
        }

#endregion
        
#region Sort

        /// <summary>
        /// 应用排序
        /// </summary>
        protected virtual void ApplyReorder() {}

        /// <summary>
        /// 排序
        /// </summary>
        /// <param name="iAscending">true:升序; false:倒序;</param>
        public abstract void Sort(bool iAscending);

#endregion

#region SearchBar

        /// <summary>
        /// 显示检索Bar
        /// </summary>
        public bool SearchBarVisible { get; protected set; } = false;

        /// <summary>
        /// 检索Bar
        /// </summary>
        protected SearchField SearchBar { get; private set; } = null;

        /// <summary>
        /// 检索Key
        /// </summary>
        public string SearchKey { get; protected set; } = null;

        /// <summary>
        /// 检索中标志位
        /// </summary>
        public bool Searching { get; protected set; } = false;
        
        /// <summary>
        /// 检索列表
        /// </summary>
        protected List<SearchCapsule<TType>> SearchResult { get; set; } = null;

        /// <summary>
        /// 显示检索Bar
        /// </summary>
        /// <param name="iVisible">true:显示; false:不显示;</param>
        public void ShowSearchBar(bool iVisible)
        {
            SearchBarVisible = iVisible;
            if (!iVisible)
            {
                Searching = false;
                SearchResult = null;
                RefreshElementCaptions();
            }
        }

        /// <summary>
        /// 执行检索
        /// </summary>
        /// <param name="iSearchKey">检索Key</param>
        protected abstract List<SearchCapsule<TType>> DoSearchByKey(string iSearchKey);

#endregion

#region Draw
        
        /// <summary>
        /// 绘制自身
        /// </summary>
        /// <param name="iRect">绘制区域</param>
        /// <param name="iEvent">Event</param>
        /// <param name="iSelf">自身Property</param>
        /// <returns>绘制范围</returns>
        protected override Rect DrawSelf(Rect iRect, Event iEvent, SerializedProperty iSelf)
        {
            var displayRect = iRect;
            displayRect.y += StartPosYOffset;
            
            var headerRect = displayRect;
            headerRect.height = HeaderHeight;
            headerRect = DrawHeader(headerRect, iEvent);

            // 显示检索Bar
            var posY = headerRect.yMax - 2;
            var searchBarRect = headerRect;
            searchBarRect.xMin += HorizontalSpacing;
            searchBarRect.xMax -= HorizontalSpacing;
            if (SearchBarVisible)
            {
                searchBarRect.yMin = headerRect.yMax;
                searchBarRect.yMax = searchBarRect.yMin + SearchBarHeight;
                searchBarRect.yMin -= 2;
                searchBarRect = DrawSearchBar(searchBarRect, iEvent);
                posY = searchBarRect.yMax - 2;
            }
            
            // 列表背景
            var listBgRect = searchBarRect;
            listBgRect.yMin = posY;
            listBgRect.yMax = displayRect.yMax - FooterHeight - 2;
            if (!IsArray || 0 >= VisibleSize)
            {
                listBgRect.height = EmptyListHeight;
            }
            
            if (Draggable)
            {
                listBgRect = UtilsGUIEx.DrawDraggableBg(listBgRect, iEvent, false, false);
                HandleDragAndDrop(listBgRect, iEvent, OnAssetsDragAndDropDown);
            }
            else
            {
                listBgRect = UtilsGUIEx.DrawGenericBg(listBgRect, iEvent);
            }
                
            var listRect = listBgRect;
            listRect.xMax -= 1;
            if (SearchBarVisible)
            {
                listRect.yMin += 1;
            }
            if (!IsArray)
            {
                var emptyDisplayRect = UtilsGUIEx.DrawLabel(listRect, iEvent, _emptyLabelContent, GuiExHelper.MiddleLeftLabel);
            }
            else if (0 >= VisibleSize)
            {
                if (Searching)
                {
                    _emptyLabelContent.text = "未检查到目标";
                }
                var emptyDisplayRect = UtilsGUIEx.DrawLabel(listRect, iEvent, _emptyLabelContent, GuiExHelper.MiddleLeftLabel);
            }
            else
            {
                if (SearchBarVisible)
                {
                    listRect.yMin -= 1;
                }

                if (Draggable)
                {
                    listRect.xMin += 1;
                    listRect.xMax -= 1;
                }
                
                listRect = DrawElements(listRect, iEvent);
            }
            
            // Footer
            if (FooterVisible)
            {
                var footerRect = listBgRect;
                footerRect.yMin = listBgRect.yMax + 1;
                footerRect.yMax = displayRect.yMax - 1;
                footerRect = DrawFooter(footerRect, iEvent);
            }
            
            return displayRect;
        }
        
#region SearchBar

        /// <summary>
        /// 绘制标题
        /// </summary>
        /// <param name="iRect">绘制矩形区域</param>
        /// <param name="iEvent">Event</param>
        /// <returns>绘制的矩形区域</returns>
        private Rect DrawSearchBar(Rect iRect, Event iEvent)
        {
            var displayRect = iRect;
            // 背景
            var bgRect = DrawGenericBg(displayRect, iEvent, (int)GUIOperateOptions.DrawBg);

            var barRect = bgRect;
            barRect.xMin += HorizontalSpacing;
            barRect.xMax -= (HorizontalSpacing + _searchBtnWidth);
            barRect.yMin += 1;
            barRect.yMax -= 1;

            if (null == SearchBar)
            {
                SearchBar = new SearchField();
            }

            var barRectTmp = barRect;
            barRectTmp.y += VerticalSpacing;
            SearchKey = SearchBar.OnGUI(barRectTmp, SearchKey);
            if (string.IsNullOrEmpty(SearchKey))
            {
                Searching = false;
                SearchResult = null;
            }

            var searchBtnRect = barRect;
            searchBtnRect.xMin = barRect.xMax;
            searchBtnRect.xMax = bgRect.xMax - HorizontalSpacing;
            searchBtnRect = UtilsGUIEx.DrawButton(searchBtnRect, iEvent, SearchBtnContent,
                () =>
                {
                    ListSelection.Clear();
                    SearchResult = DoSearchByKey(SearchKey);
                    if (null != SearchResult && 0 < SearchResult.Count)
                    {
                        Searching = true;
                        Pagination?.ResetIndex();
                        RefreshElementCaptions();
                        ListSelection.Select(0);
                        
                        OnSearched?.Invoke(SearchKey);
                    }
                }, ColorEx.Chartreuse0);
            
            return displayRect;
        }

#endregion

        /// <summary>
        /// 绘制分页头部
        /// </summary>
        /// <param name="iRect">绘制区域</param>
        /// <param name="iEvent">Event</param>
        /// <returns>绘制范围</returns>
        private Rect DrawHeader(Rect iRect, Event iEvent)
        {
            var displayRect = iRect;
            
            var count = VisibleSize;
            // 取得分页总数
            var pagesCount = Pagination.GetPageCount(count);
            var pageIndex = Mathf.Clamp(Pagination.Index, 0, pagesCount - 1);
            
            if (pageIndex != Pagination.Index) 
            {
                Pagination.Index = 0 >= pagesCount ? -1 : pageIndex;
                HandleUtility.Repaint();
            }

            var panelRect = displayRect;
            panelRect.xMin += HorizontalSpacing;
            panelRect.xMax -= HorizontalSpacing;
            // 上一页按钮区域
            var prevRect = new Rect(panelRect.xMin + HorizontalSpacing, panelRect.y, 17f, panelRect.height - 1);
            // 分页下来菜单区域
            var popupRect = new Rect(prevRect.xMax, panelRect.y, 14f, panelRect.height - 1);
            // 下一页按钮区域
            var nextRect = new Rect(popupRect.xMax, panelRect.y, 17f, panelRect.height - 1);
            
            // 分页面板背景
            if (EventType.Repaint == iEvent.type) 
            {
                GuiExHelper.PaginationHeader.Draw(panelRect, false, true, true, false);
            }
			
            _pageInfoContent.text = string.Format(GuiExHelper.PAGE_INFO_FORMAT, Pagination.Index + 1, pagesCount);

            var pageInfoRect = iRect;
            pageInfoRect.xMin = iRect.xMax - GuiExHelper.PaginationText.CalcSize(_pageInfoContent).x - 7;
			
            // 绘制分页信息
            GUI.Label(pageInfoRect, _pageInfoContent, GuiExHelper.PaginationText);
            
            //绘制前一页按钮
            EditorGUI.BeginDisabledGroup(!Pagination.IsMovePrevPageEnable());
            if (GUI.Button(prevRect, GuiExHelper.PaginationPrevBtn, GuiExHelper.PreButtonStretch)) {

                Pagination.Index = Mathf.Max(0, Pagination.Index - 1);
            }
            EditorGUI.EndDisabledGroup();
            
            // 分页下拉列表
            EditorGUI.BeginDisabledGroup(1 >= pagesCount);
            if (EditorGUI.DropdownButton(popupRect, GuiExHelper.PaginationPopup, FocusType.Passive, GuiExHelper.PreButtonStretch)) 
            {
                var menu = new GenericMenu();
                for (var i = 0; i < pagesCount; ++i) 
                {
                    menu.AddItem(new GUIContent($"Page {i + 1}"), i == Pagination.Index, OnMovePageClicked, i);
                }
                menu.DropDown(popupRect);
            }
            EditorGUI.EndDisabledGroup();
            
            // 下一页按钮
            EditorGUI.BeginDisabledGroup(!Pagination.IsMoveNextPageEnable(VisibleSize));
            if (GUI.Button(nextRect, GuiExHelper.PaginationNextBtn, GuiExHelper.PreButtonStretch)) 
            {
                Pagination.Index = Mathf.Min(pagesCount - 1, Pagination.Index + 1);
            }
            EditorGUI.EndDisabledGroup();

            var useFixedPageSize = Pagination.PageSize > 0;
            var currentPageSize = Pagination.PageSize;
            EditorGUI.BeginDisabledGroup(useFixedPageSize);
            _pageSizeContent.text = currentPageSize.ToString();

            var style = GuiExHelper.PageSizeTextField;
            var icon = GuiExHelper.ListIcon.image;

            float labelWidth = icon.width + 2;
            var width = style.CalcSize(_pageSizeContent).x + 50;

            var pageSizeRect = iRect;
            pageSizeRect.x = iRect.center.x - (width - labelWidth) / 2;
            pageSizeRect.width = width;

            EditorGUI.BeginChangeCheck();

            EditorGUIUtility.labelWidth = labelWidth;
            EditorGUIUtility.SetIconSize(new Vector2(icon.width, icon.height));
            var newPageSize = EditorGUI.DelayedIntField(pageSizeRect, GuiExHelper.ListIcon, currentPageSize, style);

            EditorGUIUtility.labelWidth = 0;
            EditorGUIUtility.SetIconSize(Vector2.zero);

            if (EditorGUI.EndChangeCheck()) {

                Pagination.PageSize = Mathf.Clamp(newPageSize, 0, count);
                Pagination.Index = Mathf.Min(Pagination.GetPageCount(count) - 1, Pagination.Index);
            }

            EditorGUI.EndDisabledGroup();
            
            return displayRect;
        }

        /// <summary>
        /// 绘制Element列表
        /// </summary>
        /// <param name="iRect">绘制矩形区域</param>
        /// <param name="iEvent">Event</param>
        /// <returns>绘制范围</returns>
        protected abstract Rect DrawElements(Rect iRect, Event iEvent);

        /// <summary>
        /// 绘制分页头部
        /// </summary>
        /// <param name="iRect">绘制区域</param>
        /// <param name="iEvent">Event</param>
        /// <returns>绘制范围</returns>
        private Rect DrawFooter(Rect iRect, Event iEvent)
        {
            var displayRect = iRect;
            
            var footerRect = displayRect;
            footerRect.yMin = displayRect.yMin + 1;
            footerRect.yMax = displayRect.yMax - 1;
            var disabled = !Removable || 0 >= VisibleSize;
            EditorGUI.BeginDisabledGroup(disabled);
            var delBtnRect = footerRect;
            delBtnRect.xMax -= HorizontalSpacing;
            delBtnRect.xMin = delBtnRect.xMax - BtnWidthIconOnly - HorizontalSpacing * 2;
            delBtnRect = UtilsGUIEx.DrawFooterButton(delBtnRect, iEvent, GuiExHelper.DelIconBtn,
                OnDelElementBtnClicked, GuiExHelper.PreButton);
            EditorGUI.EndDisabledGroup();

            if (Addable)
            {
                var addBtnRect = delBtnRect;
                addBtnRect.x = delBtnRect.xMin - BtnWidthIconOnly - HorizontalSpacing * 4;
                addBtnRect = UtilsGUIEx.DrawFooterButton(addBtnRect, iEvent, GuiExHelper.AddIconBtn,
                    OnAddElementBtnClicked, GuiExHelper.PreButton);
            }
            
            return displayRect;
        }
        
#endregion
        
    }
}
